package com.ubxtech.websocket.util;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson2.JSONArray;
import com.alibaba.fastjson2.JSONObject;
import com.ubxtech.core.context.Result;
import com.ubxtech.core.utils.StrPool;
import lombok.extern.slf4j.Slf4j;

import jakarta.websocket.Session;
import java.io.IOException;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;

/**
 * @author Victor.Xiao
 * @since 2023-11-27 15:23
 **/
@Slf4j
public class SocketUtil {
    public static final String WEBSOCKET_PARAM = "webSocketParam";

    public static <T> Boolean sendMessage(String toUserId,Result<T> result,ConcurrentHashMap<String, Map<String,Session>> sessionMap) {
        return sendMessage(toUserId, "", result, sessionMap);
    }
    public static <T> Boolean sendMessage(String toUserId,String token,Result<T> result,ConcurrentHashMap<String, Map<String,Session>> sessionMap) {
        if (CollectionUtil.isEmpty(sessionMap) || result == null) {
            return true;
        }
        Map<String,Session> tokenSession = sessionMap.get(toUserId);
        String msg = JSONObject.toJSONString(result);
        if (StrUtil.isNotBlank(token)) {
            if (CollectionUtil.isNotEmpty(tokenSession)) {
                log.debug("SocketUtil sendMessage is token 发送给{}消息,内容是:{}",token,msg);
                return sendMessage(tokenSession.get(token), msg);
            }
        } else {
            log.debug("SocketUtil sendMessage not token 发送给{}消息,内容是:{}",tokenSession.keySet(),msg);
            sendMessage(tokenSession.values(), msg);
        }
        return true;
    }


    /**
     *
     * 根据Key匹配规则,例如sessionKey是aaa-bbb,aaa
     * 如果能精准找到key则直接发给对应的session
     * 例如给bbb,则会匹配找到aaa-bbb的key
     * @author Victor.Xiao
     * @since 2023-12-26 16:16
     * @param toUserId toUserId
     * @param sessionMap sessionMap
     * @return java.util.List<jakarta.websocket.Session>
    */
    public static List<Session> getSessions(String toUserId,ConcurrentHashMap<String, Map<String,Session>> sessionMap) {
        List<Session> list = new ArrayList<>();
        if (CollectionUtil.isEmpty(sessionMap) && StrUtil.isBlank(toUserId)) {
            return list;
        }
        Map<String,Session> tokenSession = sessionMap.get(toUserId);
        if (tokenSession != null) {
            log.debug("SocketUtil sendMessage getSessions toUserId {},发送给{}消息", toUserId, tokenSession.keySet());
            list.addAll(tokenSession.values());
            return list;
        }

        for (Map.Entry<String, Map<String,Session>> entry : sessionMap.entrySet()) {
            String key = entry.getKey();
            if (key.contains(StrPool.DASH)) {
                String[] keyArr = key.split(StrPool.DASH);
                for (String ka : keyArr) {
                    if (toUserId.equalsIgnoreCase(ka)) {
                        log.debug("SocketUtil sendMessage getSessions  StrPool.DASH {},发送给{}消息", toUserId, entry.getValue().keySet());
                        list.addAll(entry.getValue().values());
                    }
                }
            }
        }
        return list;
    }



    /**
     * 根据Key匹配规则,例如sessionKey是aaa-bbb,aaa
     * 如果能精准找到key则直接发给对应的session
     * 例如给bbb,则会匹配找到aaa-bbb的key
     * @author Victor.Xiao
     * @since 2023-12-26 16:13
     * @param toUserId toUserId
     * @param message message
     * @param sessionMap sessionMap
     * @return java.lang.Boolean
    */
    public static Boolean sendMessagePattern(String toUserId,String message,ConcurrentHashMap<String, Map<String,Session>> sessionMap) {
        List<Session> list = getSessions(toUserId, sessionMap);
        sendMessage(list, message);
        return true;
    }


    /**
     * 支持给所有人发送,但sessionKey不能包含- ,因为目前可能包含sn单独连接的情况
     * @author Victor.Xiao
     * @since 2023-12-27 16:21
     * @param toUserId toUserId
     * @param message message
     * @param sessionMap sessionMap
     * @return java.lang.Boolean
    */
    public static Boolean sendMessage(String toUserId,String message,ConcurrentHashMap<String, Map<String,Session>> sessionMap) {
        if (CollectionUtil.isEmpty(sessionMap)) {
            return true;
        }
        //给所有人发送
        if (StrUtil.isBlank(toUserId)) {

            log.debug("SocketUtil sendMessage sendMessage  all user发送给{}消息", sessionMap.keySet());
            for (Map.Entry<String, Map<String,Session>> entry : sessionMap.entrySet()) {
//                //sessionKey不能包含- ,因为目前可能包含sn单独连接的情况    2024年4月1日15:32:24 现在加入了企业号,sn也发送
//                if (entry.getKey().contains(StrPool.DASH)) {
//                    continue;
//                }
                sendMessage(entry.getValue().values(), message);
            }
            return true;
        }

        //指定人发送
        Map<String,Session> tokenSession = sessionMap.get(toUserId);
        if (tokenSession != null) {
            log.debug("SocketUtil sendMessage sendMessage  one user {},发送给{}消息", toUserId, sessionMap.keySet());
            sendMessage(tokenSession.values(), message);
        }
        return true;
    }

    public static Boolean sendMessage(Collection<Session> sessions, String message) {
        if (CollectionUtil.isEmpty(sessions)) {
            return true;
        }
        for (Session session : sessions) {
            sendMessage( session,  message);
        }
        return true;
    }

    public static Boolean sendMessage(Session session, String message) {
        if (StrUtil.isBlank(message) || session == null) {
            log.error("SocketUtil sendMessage 发送webSocket参数为空message:{}, session:{}", message, session);
            return true;
        }
        try {
            session.getBasicRemote().sendText(message);
        } catch (IOException ignore) {
            log.error("SocketUtil sendMessage 发送webSocket异常 message:{}, session:{}", message, session);
            return false;
        }
        return true;
    }


    public static void closeAll(ConcurrentHashMap<String, Map<String,Session>> sessionMap) {

        for (Map.Entry<String, Map<String,Session>> sessionEntry : sessionMap.entrySet()) {
            Map<String,Session> tokenSession = sessionEntry.getValue();
            for (Map.Entry<String, Session> tokenSessionEntry : tokenSession.entrySet()) {
                try {
                    tokenSessionEntry.getValue().close();
                    log.info("SocketUtil 当前服务端主动断开所有连接 {} 的连接,token:{}", sessionEntry.getKey(),tokenSessionEntry.getKey());
                } catch (Exception e) {
                    log.error("SocketUtil 服务端主动断开所有连接 " + sessionEntry.getKey() + "连接发生异常,token:" + tokenSessionEntry.getKey(), e);
                }
            }
        }
    }


    public static void close(List<String> userIds,ConcurrentHashMap<String, Map<String,Session>> sessionMap) {
        for (String userId : userIds) {
            if (sessionMap.containsKey(userId)) {
                Map<String,Session> tokenSession = sessionMap.get(userId);
                log.info("SocketUtil 服务端主动断开{}的连接", userId);
                for (Map.Entry<String, Session> tokenSessionEntry : tokenSession.entrySet()) {
                    try {
                        tokenSessionEntry.getValue().close();
                        log.info("SocketUtil 当前服务端主动断开连接 {} 的连接,token:{}", userIds,tokenSessionEntry.getKey());
                    } catch (Exception e) {
                        log.error("SocketUtil 服务端主动断开连接 " + userIds + "连接发生异常,token:" + tokenSessionEntry.getKey(), e);
                    }
                }
            }
        }
    }


    public static void closeDuplicateUser(String userId,ConcurrentHashMap<String,  Map<String,Session>> sessionMap){
        Map<String,Session> tokenSession = sessionMap.get(userId);
        if (tokenSession==null ){
            return;
        }
        if (tokenSession.size() > 0) {
            String msg = JSONObject.toJSONString(new Result<>(WebSocketErrorCode.DUPLICATE_LOGIN));
            log.info("SocketUtil 关闭重复连接,userId:{},sessionMap:{}",userId,sessionMap.keySet());
            for (Map.Entry<String, Session> tokenSessionEntry : tokenSession.entrySet()) {
                try {
                    SocketUtil.sendMessage(tokenSessionEntry.getValue(), msg);
                    tokenSessionEntry.getValue().close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            sessionMap.remove(userId);
        }
    }

    public static void closeDuplicateUser(String userId,String token,ConcurrentHashMap<String,  Map<String,Session>> sessionMap){
        Map<String,Session> tokenSession = sessionMap.get(userId);
        if (tokenSession==null ){
            return;
        }
        Session session = tokenSession.get(token);
        if (session != null) {
            log.info("--------- SocketUtil 关闭重复连接,userId:{},token:{}", userId, token);
            try {
                SocketUtil.sendMessage(session, JSONObject.toJSONString(new Result<>(WebSocketErrorCode.DUPLICATE_LOGIN)));
                session.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
//            sessionMap.remove(userId);
            //按token移除对应session
            tokenSession.remove(token);
            if (CollectionUtil.isEmpty(tokenSession)) {
                sessionMap.remove(userId);
            }
        }
    }

    public static void addSession(String userId, String token, Session session,ConcurrentHashMap<String, Map<String,Session>> sessionMap) {
        //强制关闭之前重名的连接 , 保证一个用户一个连接
        SocketUtil.closeDuplicateUser(userId,token,sessionMap);
        Map<String,Session> tokenSession = sessionMap.computeIfAbsent(userId, k -> new HashMap<>());
        tokenSession.put(token, session);
        SocketUtil.sendMessage(session, JSONObject.toJSONString(new Result<>("connected")));
        Set<String> onlineToken = new HashSet<>();
        for (Map.Entry<String, Map<String,Session>> sessionEntry : sessionMap.entrySet()) {
            Map<String, Session> onlineSessionMap = sessionEntry.getValue();
            onlineToken.addAll(onlineSessionMap.keySet());
        }
        log.info("--------- new connect,  userId: {} ,token: {},  online user: {},online session :{}", userId,token, JSONArray.toJSONString(sessionMap.keys()),JSONArray.toJSONString(onlineToken));
    }

}
